package userInterface;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.*;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import assembuler.Assembuler;

public class LogoScriptEditorFrame extends JFrame {	

	class LogoScriptEditorToolBar extends JPanel implements ActionListener {
		static final private String ACTION_SAVE = "save";
		static final private String ACTION_SAVEAS = "saveas";
		static final private String ACTION_EXECUTE = "execute";
		private LogoScriptEditorFrame parentFrame;
		public LogoScriptEditorToolBar(LogoScriptEditorFrame f) {
			super(new BorderLayout());
			parentFrame=f;
			JToolBar toolBar=new JToolBar("EditorToolBar");
			addButtons(toolBar);
			toolBar.setFloatable(false);
			setPreferredSize(new Dimension(200,70));
			add(toolBar,BorderLayout.PAGE_START);
		}


		protected void addButtons(JToolBar toolBar) {
			JButton button=null;
			button = makeNavigationButton("images/save.gif",ACTION_SAVE,"Save this Script","Save");
			toolBar.add(button);
			button = makeNavigationButton("images/saveas.gif",ACTION_SAVEAS,"Save As this Script","Save AS");
			toolBar.add(button);
			button = makeNavigationButton("images/execute.gif",ACTION_EXECUTE,"Execute the LogoScript","Execute");
			toolBar.add(button);
		}
		protected JButton makeNavigationButton(String imgLocation,
				String actionCommand,
				String toolTipText,
				String altText) {
			//Look for the image.

			//Create and initialize the button.
			JButton button = new JButton();
			button.setActionCommand(actionCommand);
			button.setToolTipText(toolTipText);
			button.addActionListener(this);

			button.setMaximumSize(new Dimension(60,60));
			URL tmp=LogoScriptEditorToolBar.class.getResource(imgLocation);
			if (tmp != null)
				button.setIcon(new ImageIcon(tmp));
			else
				System.err.println(imgLocation+"NOT FOUND!");

			return button;
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			String cmd=e.getActionCommand();
			if (cmd.equals(ACTION_EXECUTE)) {
				String[] lineString=textarea.getText().split("\n");
				String code=new String();
				for (int i=0;i<lineString.length;++i) {
					if (lineString[i].indexOf("//")!=-1) {
						code=code.concat(lineString[i].substring(0,lineString[i].indexOf("//")));
					} else {
						code=code.concat(lineString[i]);
					}
				}
				Assembuler.executeSimpleCodeBlocks(code);
			}
			if (cmd.equals(ACTION_SAVEAS)) {
				JFileChooser saveFileChooser=new JFileChooser(".");
				saveFileChooser.setAcceptAllFileFilterUsed(false);
				saveFileChooser.addChoosableFileFilter(new ImageFileFilter("logo"));
				int result=saveFileChooser.showSaveDialog(parentFrame);
				if (result==JFileChooser.APPROVE_OPTION) {
					File tmp=saveFileChooser.getSelectedFile();
					boolean writeFile=true;
					if (tmp.exists()) {
						int t=JOptionPane.showConfirmDialog(parentFrame, String.format("Would you Want to Rewrite %s?",tmp.getName()),"ReWrite?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
						if (t==0)
							writeFile=true;
						else
							writeFile=false;
					}
					if (writeFile) {
						if (tmp!=null) {
							try {
								if (!tmp.exists())
									tmp.createNewFile();
								BufferedWriter output=new BufferedWriter(new FileWriter(tmp));
								output.write(textarea.getText());
								output.close();
							} catch (IOException e1) {
								e1.printStackTrace();
							}
							System.out.format("Save Script to %s",tmp.getAbsolutePath());
						}
					}
				}
			}
			if (cmd.equals(ACTION_SAVE)) {
				if (filePath==null) {
					JFileChooser saveFileChooser=new JFileChooser(".");
					saveFileChooser.setAcceptAllFileFilterUsed(false);
					saveFileChooser.addChoosableFileFilter(new ImageFileFilter("logo"));
					int result=saveFileChooser.showSaveDialog(parentFrame);
					if (result==JFileChooser.APPROVE_OPTION) {
						File tmp=saveFileChooser.getSelectedFile();
						boolean writeFile=true;
						if (tmp.exists()) {
							int t=JOptionPane.showConfirmDialog(parentFrame, String.format("Would you Want to Rewrite %s?",tmp.getName()),"ReWrite?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
							if (t==0)
								writeFile=true;
							else
								writeFile=false;
						}
						if (writeFile) {
							filePath=tmp;
							parentFrame.setTitle(filePath.getName());
							fileModified=false;
						}
					}

				}
				if (filePath!=null) {
					try {
						if (!filePath.exists())
							filePath.createNewFile();
						BufferedWriter output=new BufferedWriter(new FileWriter(filePath));
						output.write(textarea.getText());
						output.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
					System.out.format("Save Script to %s",filePath.getAbsolutePath());
				}
			}

		}

	}





	class SyntaxHighlighter implements DocumentListener {
		private Set<String> keywords;
		private Style keywordStyle;
		private Style normalStyle;
		private Style commentStyle;
		public SyntaxHighlighter() {
			keywordStyle=((StyledDocument)textarea.getDocument()).addStyle("Keyword_style", null);
			normalStyle=((StyledDocument)textarea.getDocument()).addStyle("Keyword_style", null);
			commentStyle=((StyledDocument)textarea.getDocument()).addStyle("Keyword_style", null);
			StyleConstants.setForeground(keywordStyle, Color.red);
			StyleConstants.setForeground(normalStyle, Color.black);
			StyleConstants.setForeground(commentStyle, Color.green);

			keywords=new HashSet<String>();
			keywords.add("rt");
			keywords.add("lt");
			keywords.add("fd");
			keywords.add("bk");
			keywords.add("cls");
			keywords.add("setd");
			keywords.add("setw");
			keywords.add("pu");
			keywords.add("pd");
			keywords.add("setxy");
			keywords.add("setpc");
			keywords.add("setbg");
			keywords.add("stampoval");
			keywords.add("repeat");
			keywords.add("source");
		}


		public void colouring(StyledDocument doc, int pos, int len) throws BadLocationException {
			// 取得插入或者删除后影响到的单词.
			// 例如"public"在b后插入一个空格, 就变成了:"pub lic", 这时就有两个单词要处理:"pub"和"lic"
			// 这时要取得的范围是pub中p前面的位置和lic中c后面的位置
			int start = indexOfLineStart(doc, pos);
			int end = indexOfLineEnd(doc, pos + len);

			while (start < end) {
				if (isWordCharacter(doc,start)) {
					// 如果是以字母或者下划线开头, 说明是单词
					// pos为处理后的最后一个下标
					start = colouringWord(doc, start);
				} else {
					SwingUtilities.invokeLater(new ColouringTask(doc, start, 1, normalStyle));
					++start;
				}
			}
		}
		public void colouringComment(StyledDocument doc,int pos,int len) throws BadLocationException {
			int start=indexOfLineStart(doc,pos);
			int end=indexOfLineEnd(doc,pos+len);
			String totalString=doc.getText(start, end-start);
			String[] lineString=totalString.split("\n");
			int tpos=0;
			for (int i=0;i<lineString.length;++i) {
				if (lineString[i].indexOf("//")!=-1) {
					SwingUtilities.invokeLater(new ColouringTask(doc,lineString[i].indexOf("//")+tpos+start,lineString[i].length()-lineString[i].indexOf("//"),commentStyle));
				}
				tpos=tpos+lineString[i].length()+1;
			}
		}

		public int colouringWord(StyledDocument doc, int pos) throws BadLocationException {
			int wordEnd = indexOfWordEnd(doc, pos);
			String word = doc.getText(pos, wordEnd - pos);
			if (keywords.contains(word.toLowerCase())) {
				// 如果是关键字, 就进行关键字的着色, 否则使用普通的着色.
				// 这里有一点要注意, 在insertUpdate和removeUpdate的方法调用的过程中, 不能修改doc的属性.
				// 但我们又要达到能够修改doc的属性, 所以把此任务放到这个方法的外面去执行.
				// 实现这一目的, 可以使用新线程, 但放到swing的事件队列里去处理更轻便一点.
				SwingUtilities.invokeLater(new ColouringTask(doc, pos, wordEnd - pos, keywordStyle));
			} else {
				SwingUtilities.invokeLater(new ColouringTask(doc, pos, wordEnd - pos, normalStyle));
			}

			return wordEnd;
		}
		public int indexOfWordEnd(Document doc,int pos) throws BadLocationException {
			for (;isWordCharacter(doc,pos);++pos);

			return pos;
		}
		public char getCharAt(Document doc, int pos) throws BadLocationException {
			return doc.getText(pos, 1).charAt(0);
		}
		public int indexOfLineStart(Document doc, int pos) throws BadLocationException {

			for (; pos > 0 && !isWordSlashN(doc, pos - 1); --pos);

			return pos;
		}
		public int indexOfLineEnd(Document doc, int pos) throws BadLocationException {

			for (;pos<doc.getLength() && !isWordSlashN(doc, pos); ++pos);

			return pos;
		}
		public boolean isWordCharacter(Document doc,int pos) throws BadLocationException {
			char ch=getCharAt(doc,pos);
			if (Character.isLetter(ch) || ch=='_') return true;
			return false;
		}
		public boolean isWordSlashN(Document doc, int pos) throws BadLocationException {
			char ch = getCharAt(doc, pos);
			if (ch == '\n') { return true; }
			return false;
		}
		private class ColouringTask implements Runnable {
			private StyledDocument doc;
			private Style style;
			private int pos;
			private int len;

			public ColouringTask(StyledDocument doc, int pos, int len, Style style) {
				this.doc = doc;
				this.pos = pos;
				this.len = len;
				this.style = style;
			}

			public void run() {
				try {
					// 这里就是对字符进行着色
					doc.setCharacterAttributes(pos, len, style, true);
				} catch (Exception e) {}
			}
		}


		@Override
		public void insertUpdate(DocumentEvent e) {
			try {
				colouring((StyledDocument) e.getDocument(), e.getOffset(), e.getLength());
				colouringComment(((StyledDocument) e.getDocument()),e.getOffset(),e.getLength());
				fileModified=true;
			} catch (BadLocationException e1) {
				e1.printStackTrace();
			}
		}

		@Override
		public void removeUpdate(DocumentEvent e) {
			try {
				// 因为删除后光标紧接着影响的单词两边, 所以长度就不需要了
				colouring((StyledDocument) e.getDocument(), e.getOffset(), 0);
				colouringComment(((StyledDocument) e.getDocument()),e.getOffset(),e.getLength());
				fileModified=true;
			} catch (BadLocationException e1) {
			}

		}

		@Override
		public void changedUpdate(DocumentEvent e) {
		}

	}


	class MyWindowListener extends WindowAdapter {

		public void windowClosing(WindowEvent e) {
			if (fileModified) {
				int t=JOptionPane.showConfirmDialog((LogoScriptEditorFrame)e.getSource(), "Do you want to close the window without SAVE?","QUIT?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
				if (t==0) {
					((LogoScriptEditorFrame)e.getSource()).dispose();
				}
			} else {
				((LogoScriptEditorFrame)e.getSource()).dispose();
			}
		}
		
		
	}

	protected JTextPane textarea;
	private File filePath; 
	private boolean fileModified;
	private SyntaxHighlighter syntaxHighlighter;
	public LogoScriptEditorFrame(String title,File f) {
		super(title);
		filePath=f;
		textarea=new JTextPane();
		fileModified=false;
		this.setBounds(0, 0, 500, 375);
		this.setLocationRelativeTo(EnvironmentVariables.mainFrame);
		syntaxHighlighter=new SyntaxHighlighter();
		textarea.getDocument().addDocumentListener(syntaxHighlighter);
		this.addWindowListener(new MyWindowListener());
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		textarea.setSize(new Dimension(EnvironmentVariables.editorColumns,EnvironmentVariables.editorRows));
		JScrollPane textWithScrollBar=new JScrollPane(textarea);
		textWithScrollBar.setHorizontalScrollBar(textWithScrollBar.createHorizontalScrollBar());
		textWithScrollBar.setVerticalScrollBar(textWithScrollBar.createVerticalScrollBar());
		textWithScrollBar.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		textWithScrollBar.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		getContentPane().add(new LogoScriptEditorToolBar(this),BorderLayout.PAGE_START);
		getContentPane().add(textWithScrollBar,BorderLayout.CENTER);
		if (filePath!=null) {
			BufferedReader br;
			String line=null;
			String tmp="";
			try {
				br = new BufferedReader(new FileReader(filePath));
				try {
					while ((line=br.readLine())!=null) {
						tmp=tmp.concat(line).concat("\n");
					}
					textarea.setText(tmp);
				} catch (IOException e) {
					e.printStackTrace();
				}
			} catch (FileNotFoundException e1) {
				e1.printStackTrace();
			}		
			initHighlight();
			fileModified=false;
		}
		setVisible(true);
	}

	private void initHighlight() {
		try {
			syntaxHighlighter.colouring(((StyledDocument)textarea.getDocument()),0,textarea.getDocument().getLength());
			syntaxHighlighter.colouringComment((StyledDocument)textarea.getDocument(),0,textarea.getDocument().getLength());
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}

	

}



